package tg.game.util.timeline;

import java.util.LinkedList;
import java.util.ListIterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



/**
 * 时间线:可以在时间线上添加指定时间点执行的任务，只用一个线程来当做时间线，执行到时间点的任务
 * 目的：单线程来安排固定时间点执行的任务，减小线程数,   任务可以多次执行，由自己实现接口算出下次执行时间点，或彻底结束 不再执行
 * @author NKW
 * 实现原理：
 *    所有的执行任务按执行时间排序
 *    
 *    每次添加任务都要锁定队列，找到自己的时间点位置，插入后再刷队列
 *    
 *    每次执行完当前任务，都要刷一下队列
 *    
 *    刷队列（锁队列）：
 *        如果第一个任务的时间点已经到了，那就执行，
 *        如果没有到，则为排在第一个的任务定时剩下的时间来准备执行
 */
public class TimeLine extends Thread{
	private static TimeLine instance = new TimeLine();//静态类
	private ExecutorService executor = Executors.newCachedThreadPool();//线程池
	private LinkedList<TimeTask> taskList =  new LinkedList<TimeTask>();//任务队列
	
	private TimeLine() {
		this.start();//执行本时间线
	}
	/**
	 * 
	 */
	@Override
	public void run() {
		while(true){
			this.execTaskList();
			try {
				sleep(20);//没有任务时才会在这里等，有任务去执行时，都wait那里了
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public static TimeLine getInstance(){
		return instance;
	}
	/**
	 * 添加任务
	 */
	public static void addTask(TimeTask task){
		instance.insertTaskByTime(task);//插入任务队列
	}
	/**
	 * 取消任务
	 */
	public synchronized static void cancelTask(TimeTask task){
		task.cancel();//取消此任务
		instance.notifyAll();//唤醒队列，重新执行最前面的（会把取消的踢除）
	}
	/**
	 * 获取第一个任务
	 * @return
	 */
	public TimeTask getTopTask(){
		return this.taskList.getFirst();
	}
	/**
	 * 执行任务队列：
	 *    从头取，遇到取消的直接跳过，遇到时间到的就执行，如果第一个没取消的时间没到，就直接等到它的执行时间到为止
	 */
	private synchronized void execTaskList(){
		if (this.taskList.size() == 0) return;//如果一个任务都没有，直接返回
		TimeTask topTask = this.taskList.getFirst();
		while (topTask.isCancel()){//如果一直都是取消的状态，就一直取头一个
			this.taskList.removeFirst();
			if (this.taskList.size() == 0) return;//如果一个任务都没有，直接返回
			topTask = this.taskList.getFirst();//重新取得头一个
		}	
		long duration = topTask.getNextExecTime() - System.currentTimeMillis();
		if (duration <= 10){//任务执行时间已经超过现在的时间了
			//如果第一个已经可以执行了，直接执行它
			this.taskList.remove(topTask);//移出队列，要执行任务了
			executor.execute(topTask);//然后执行任务
		}else{//还没到时间，直接等待
			try {
				this.wait(duration);//直接等待时间来执行
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * 插入一个任务
	 * @param task
	 * 按此任务的时间插入到所有任务队列中相应的时间位置
	 */
	private synchronized void insertTaskByTime(TimeTask task){
		this.taskList.remove(task);//先移走
		if (this.taskList.size() == 0){
			this.taskList.add(task);
			return;//如果一个任务都没有的话，直接返回去
		}
		ListIterator<TimeTask> it = taskList.listIterator();
		TimeTask previous = this.taskList.getFirst();//第一个
		boolean insertFlag = false;
		while (it.hasNext()) {
			TimeTask nextTT = it.next();
			if (previous.getNextExecTime() <= task.getNextExecTime()
					&&  task.getNextExecTime() <= nextTT.getNextExecTime()){
				//如果当前的任务时间是在前后中间，就插进去
				it.add(task);
				insertFlag = true;//有插入成功了
			}
		}
		if (!insertFlag)
			this.taskList.addLast(task);//如果都没有插入成功的，直接就插入到最后
	}
	
}
